package com.scs.application.modules.dept.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.pdf.PdfExportUtils;
import com.scs.application.core.pdf.entity.PdfExportParams;
import com.scs.application.core.pdf.enums.PdfPageDirection;
import com.scs.application.core.query.QueryContext;
import com.scs.application.core.utils.BeanUtils;
import com.scs.application.core.utils.DateUtils;
import com.scs.application.modules.base.entity.Dept;
import com.scs.application.modules.base.entity.Warehouse;
import com.scs.application.modules.base.service.DeptService;
import com.scs.application.modules.base.service.WarehouseService;
import com.scs.application.modules.dept.dto.InOutDTO;
import com.scs.application.modules.dept.entity.DailyBalance;
import com.scs.application.modules.dept.entity.DailyBalanceItem;
import com.scs.application.modules.dept.mapper.DailyBalanceMapper;
import com.scs.application.modules.dept.service.DailyBalanceItemService;
import com.scs.application.modules.dept.service.DailyBalanceService;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.modules.wm.dto.DailyFsdKey;
import com.scs.application.modules.wm.dto.DailyOrderItemDTO;
import com.scs.application.modules.wm.dto.DailyOrderKey;
import com.scs.application.modules.wm.dto.DailyQtyEndDTO;
import com.scs.application.modules.wm.entity.Stock;
import com.scs.application.modules.wm.entity.WorkOrder;
import com.scs.application.modules.wm.entity.WorkOrderItem;
import com.scs.application.modules.wm.enums.WorkOrderType;
import com.scs.application.modules.wm.mapper.StockMapper;
import com.scs.application.modules.wm.mapper.WorkOrderMapper;
import com.scs.application.modules.wm.service.StockService;
import com.scs.application.modules.wm.service.WorkOrderItemService;
import com.scs.application.modules.wm.service.WorkOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 仓库日清表 服务实现类
 * </p>
 */
@Service
public class DailyBalanceServiceImpl extends BaseServiceImpl<DailyBalanceMapper, DailyBalance> implements DailyBalanceService {

    @Autowired
    private StockService stockService;
    @Autowired
    private WorkOrderService workOrderService;

    @Autowired
    private WorkOrderMapper workOrderMapper;
    @Autowired
    private WorkOrderItemService workOrderItemService;
    @Autowired
    private WarehouseService warehouseService;
    @Autowired
    private DailyBalanceItemService dailyBalanceItemService;
    @Autowired
    private DeptService deptService;

    @Autowired
    private StockMapper stockMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void syncDailyBalance() {
        List<Warehouse> list = warehouseService.list();
//        获取上一天的，开始日清
        String now = String.valueOf(LocalDate.now().minusDays(1));

        for (Warehouse warehouse : list) {

            addDailyBalance(warehouse.getId(), now);
        }

    }

    public boolean addDailyBalance(String warehouseId, String balanceDate) {
        // 日清数据
        List<DailyBalanceItem> dailyBalanceItems = new ArrayList<>();
        DailyBalanceItem dailyBalanceItem;

        // 删除大于等于 当前日期 当前科室的日清数据
        List<DailyBalance> list = this.list(Wrappers.<DailyBalance>query()
                .ge("balance_date", balanceDate).eq("warehouse_id", warehouseId));
        List<String> dailyBalanceids = list.stream().map(o -> o.getId()).collect(Collectors.toList());
        delete(dailyBalanceids);

        //指定转换格式
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        //进行转换
        LocalDate date = LocalDate.parse(balanceDate, fmt);

        // 上一天的期末库存
        String beforeDate = String.valueOf(date.minusDays(1));
        // 获取上一天主表数据
        List<DailyBalance> dailyBalanceList = this.list(Wrappers.<DailyBalance>query()
                .eq("balance_date", beforeDate).eq("warehouse_id", warehouseId));
        Map<DailyOrderKey, DailyQtyEndDTO> dbiMap = new HashMap<>();
        if (!dailyBalanceList.isEmpty()) {
            // 获取上一天明细表数据
            List<DailyBalanceItem> dailyBalanceItemList = dailyBalanceItemService.list(Wrappers.<DailyBalanceItem>query()
                    .in("dept_daily_balance_id", dailyBalanceList.stream().map(o -> o.getId()).collect(Collectors.toList())));
            dbiMap = dailyBalanceItemList.stream()
                    .collect(Collectors.toMap(o -> new DailyOrderKey(o.getSupplierId(), o.getMatrId(), o.getFlagOmoc()),
                            o -> new DailyQtyEndDTO(o.getQtyEnd(), o.getAmountEnd()), (o1, o2) -> o1));
        }

        // 出入库数据汇总
//        List<WorkOrder> workOrderList = workOrderService.list(Wrappers.<WorkOrder>query()
//                .eq("DATE_FORMAT(bus_date,'%Y-%m-%d')", balanceDate).eq("warehouse_id", warehouseId).eq("flow_status", GlobalConsts.FLOW_END_VALUE));

        //获取除去调拨单中from_warehouse_id和to_warehouse_id相同的单据
        List<WorkOrder> workOrderList = workOrderMapper.dailyWorkOrder(balanceDate, warehouseId);
        // 获取今天的入库单ids 不包括冲红数量
        List<String> inIds = workOrderList
                .stream()
                .filter(o -> GlobalConsts.ORDER_DIRECTION_IN.equals(o.getDirection())
                       && !WorkOrderType.IN_USE_RED.equals(o.getBusType())
                )
                .map(o -> o.getId()).collect(Collectors.toList());
        // 获取今天的出库单ids
        List<String> outIds = workOrderList.stream().filter(o -> GlobalConsts.ORDER_DIRECTION_OUT.equals(o.getDirection()))
                .map(o -> o.getId()).collect(Collectors.toList());
        // 获取今天的病人消耗ids
        List<String> oprtIds = workOrderList.stream().filter(o -> WorkOrderType.OPRT_USE.equals(o.getBusType()))
                .map(o -> o.getId()).collect(Collectors.toList());
        // 获取今天的消耗出库ids
        List<String> useIds = workOrderList.stream().filter(o -> WorkOrderType.OUT_USE.equals(o.getBusType()))
                .map(o -> o.getId()).collect(Collectors.toList());
        // 获取今天的病人冲红ids
        List<String> oprtRedIds = workOrderList.stream().filter(o -> WorkOrderType.IN_USE_RED.equals(o.getBusType()) && o.getFlagOmoc())
                .map(o -> o.getId()).collect(Collectors.toList());
        //  获取今天的消耗冲红ids
        List<String> useRedIds = workOrderList.stream().filter(o -> WorkOrderType.IN_USE_RED.equals(o.getBusType()) && !o.getFlagOmoc())
                .map(o -> o.getId()).collect(Collectors.toList());

        //  获取今天的科室退货入库
        List<String> deptReturnIds = workOrderList.stream().filter(o -> WorkOrderType.IN_DEPT_RETURN.equals(o.getBusType()) && !o.getFlagOmoc())
                .map(o -> o.getId()).collect(Collectors.toList());

        //  入库数量/金额
        Map<DailyOrderKey, InOutDTO> inDTOMap = inOutUtil(inIds);

        //  出库数量/金额
        Map<DailyOrderKey, InOutDTO> outDTOMap = inOutUtil(outIds);
        //  病人消耗数量/金额
        Map<DailyOrderKey, InOutDTO> oprtOutMap = inOutUtil(oprtIds);
        //  出库消耗数量/金额
        Map<DailyOrderKey, InOutDTO> useOutMap = inOutUtil(useIds);
        //  科室消耗冲红数量/金额
        Map<DailyOrderKey, InOutDTO> oprtInMap = inOutUtil(oprtRedIds);
        //   病人消耗冲红数量/金额
        Map<DailyOrderKey, InOutDTO> useInMap = inOutUtil(useRedIds);
        //   科室退货冲红数量/金额
        Map<DailyOrderKey, InOutDTO> deptReturnMap = inOutUtil(deptReturnIds);
        //  调价数量/金额
        Map<DailyOrderKey, InOutDTO> modifyMap = adjustmentPriceUtil(outIds
        );
        // 库存数据汇总
        List<Stock> stockList = stockService.list(Wrappers.<Stock>query().eq("warehouse_id", warehouseId));
        Map<DailyOrderKey, List<Stock>> stockMap = stockList.stream().collect(
                Collectors.groupingBy(o -> new DailyOrderKey(o.getSupplierId(), o.getMatrId(), o.getFlagOmoc())));

        List<DailyFsdKey> fsdStockStatus = stockMapper.getFsdStockStatus(warehouseId);
        Map<DailyOrderKey, List<DailyFsdKey>> abnormalMap = fsdStockStatus.stream().collect(
                Collectors.groupingBy(o -> new DailyOrderKey(o.getSupplierId(), o.getMatrId(), o.getFlagOmoc())));
        // 日清主表
        List<Dept> deptList = deptService.list(Wrappers.<Dept>query().eq("warehouse_id", warehouseId));
        if (deptList.isEmpty()) {
            return false;
        }
        Dept dept = deptList.get(0);
        if (stockList.isEmpty()) {
            return false;
        }

        DailyBalance dailyBalance = new DailyBalance()
                .setBalanceDate(DateUtils.parse(balanceDate)).setWarehouseId(warehouseId).setWarehouseName(dept.getWarehouseName())
                .setDeptId(dept.getId()).setDeptName(dept.getName());
        this.save(dailyBalance);
        for (DailyOrderKey orderKey : stockMap.keySet()) {
            dailyBalanceItem = new DailyBalanceItem();
            List<Stock> values = stockMap.get(orderKey);
            InOutDTO inDTO = inDTOMap.get(orderKey);
            InOutDTO outDTO = outDTOMap.get(orderKey);
            InOutDTO oprtOut = oprtOutMap.get(orderKey);
            InOutDTO useOut = useOutMap.get(orderKey);
            InOutDTO oprtIn = oprtInMap.get(orderKey);
            InOutDTO useIn = useInMap.get(orderKey);
            InOutDTO deptReturn = deptReturnMap.get(orderKey);
            InOutDTO modifyOut = modifyMap.get(orderKey);
            List<DailyFsdKey> dailyFsdKeys = abnormalMap.get(orderKey);
            DailyFsdKey dailyFsdKey = new DailyFsdKey();
            if (dailyFsdKeys != null) {
                dailyFsdKey = dailyFsdKeys.get(0);
            }


            double oprtOutAmount = 0.0, oprtOutQty = 0.0, oprtInQty = 0.0, oprtInAmount = 0.0, deptReturnAmount =0.0,
                    useInAmount = 0.0, useInQty = 0.0, useOutQty = 0.0, useOutAmount = 0.0,deptReturnQty =0.0;
            if (oprtOut != null) {
                oprtOutAmount = oprtOut.getSumQmount();
                oprtOutQty = oprtOut.getSumQty();
            }
            if (oprtIn != null) {
                oprtInQty = oprtIn.getSumQty();
                oprtInAmount = oprtIn.getSumQmount();
            }
            if (useIn != null) {
                useInAmount = useIn.getSumQmount();
                useInQty = useIn.getSumQty();
            }
            if (useOut != null) {
                useOutQty = useOut.getSumQty();
                useOutAmount = useOut.getSumQmount();
            }
            if (deptReturn != null) {
                deptReturnQty = deptReturn.getSumQty();
                deptReturnAmount = deptReturn.getSumQmount();
            }


            DailyQtyEndDTO qtyEndDTO = dbiMap.get(orderKey);
            // 当前库存
            double stockMatrQty = values.stream().mapToDouble(Stock::getSkuQty).sum();
            double stockAmount = values.stream().mapToDouble(Stock::getAmount).sum();
            Stock stock = values.get(0);
            BeanUtils.copyProperties(stock, dailyBalanceItem);
            dailyBalanceItem.setId(null);
            dailyBalanceItem.setMatrId(orderKey.getMatrId())
                    .setDeptDailyBalanceId(dailyBalance.getId())
                    .setMatrSpec(stock.getMatrSpec())
                    .setMatrName(stock.getMatrName())
                    .setBalanceDate(DateUtils.parse(balanceDate))
                    .setQtyBegin(qtyEndDTO == null ? 0.0 : qtyEndDTO.getQtyEnd())
                    .setAmountBegin(qtyEndDTO == null ? 0.0 : qtyEndDTO.getQtyEndAmount())
                    .setQtyIn(inDTO == null ? 0.0 : inDTO.getSumQty())
                    .setAmountIn(inDTO == null ? 0.0 : inDTO.getSumQmount())
//                    出库 =  出库-病人冲红-科室消耗冲红
                    .setQtyOut(outDTO == null ? 0.0 : outDTO.getSumQty()-oprtInQty-useInQty-deptReturnQty)
                    .setAmountOut(outDTO == null ? 0.0 : outDTO.getSumQmount()-oprtInAmount-useInAmount-deptReturnAmount)
                    .setFlagOmoc(stock.getFlagOmoc())
                    .setSupplierId(stock.getSupplierId())
                    .setQtyStock(stockMatrQty)
                    .setAmountStock(stockAmount)
                    .setSupplierName(stock.getSupplierName());

            //期末数量   期初+入库-出库
            if (qtyEndDTO != null) {
                dailyBalanceItem.setQtyEnd(dailyBalanceItem.getQtyBegin() + dailyBalanceItem.getQtyIn() - dailyBalanceItem.getQtyOut());
                dailyBalanceItem.setAmountEnd(dailyBalanceItem.getAmountBegin() + dailyBalanceItem.getAmountIn() + dailyBalanceItem.getAmountOut());
            } else { //反推
                dailyBalanceItem.setQtyEnd(dailyBalanceItem.getQtyStock());
                dailyBalanceItem.setAmountEnd(dailyBalanceItem.getAmountStock());
                dailyBalanceItem.setQtyBegin(dailyBalanceItem.getQtyEnd() - dailyBalanceItem.getQtyIn() + dailyBalanceItem.getQtyOut());
                dailyBalanceItem.setAmountBegin(dailyBalanceItem.getAmountEnd() - dailyBalanceItem.getAmountIn() + dailyBalanceItem.getAmountOut());
            }
            dailyBalanceItem.setQtyConsumeOut(useOutQty - useInQty);
            dailyBalanceItem.setAmountConsumeOut(useOutAmount - useInAmount);
            dailyBalanceItem.setQtyCharging(oprtOutQty - oprtInQty);
            dailyBalanceItem.setAmountCharging(oprtOutAmount - oprtInAmount);
            //        调整数量   出库价格！=入库价格  出库为调整价格
            dailyBalanceItem.setQtyModify(modifyOut == null ? 0.0 : modifyOut.getSumQty());
            dailyBalanceItem.setAmountModify(modifyOut == null ? 0.0 : modifyOut.getSumQmount());
//            异常数量
            dailyBalanceItem.setQtyAbnormal(dailyFsdKey.getQty() == null ? 0.0 : dailyFsdKey.getQty());
            dailyBalanceItem.setAmountAbnormal(dailyFsdKey.getAmount() == null ? 0.0 : dailyFsdKey.getAmount());
            dailyBalanceItem.setType(dailyBalanceItem.getQtyEnd() - stockMatrQty == 0.0 ? "0" : "1");
            dailyBalanceItems.add(dailyBalanceItem);
        }
        boolean flag = dailyBalanceItemService.saveBatch(dailyBalanceItems);
        List<DailyBalanceItem> itemList = dailyBalanceItemService.list(Wrappers.<DailyBalanceItem>query().eq("dept_daily_balance_id", dailyBalance.getId()));
        dailyBalance
                .setAmountBeginSum(itemList.stream().mapToDouble(DailyBalanceItem::getAmountBegin).sum())
                .setQtyBeginSum(itemList.stream().mapToDouble(DailyBalanceItem::getQtyBegin).sum())
                .setAmountEndSum(itemList.stream().mapToDouble(DailyBalanceItem::getAmountEnd).sum())
                .setQtyEndSum(itemList.stream().mapToDouble(DailyBalanceItem::getQtyEnd).sum())
                .setQtyConsumeOutSum(itemList.stream().mapToDouble(DailyBalanceItem::getQtyConsumeOut).sum())
                .setAmountConsumeOutSum(itemList.stream().mapToDouble(DailyBalanceItem::getAmountConsumeOut).sum())
                .setQtyOutSum(itemList.stream().mapToDouble(DailyBalanceItem::getQtyOut).sum())
                .setAmountOutSum(itemList.stream().mapToDouble(DailyBalanceItem::getAmountOut).sum())
                .setAmountInSum(itemList.stream().mapToDouble(DailyBalanceItem::getAmountIn).sum())
                .setQtyInSum(itemList.stream().mapToDouble(DailyBalanceItem::getQtyIn).sum())
                .setAmountChargingSum(itemList.stream().mapToDouble(DailyBalanceItem::getAmountCharging).sum())
                .setQtyChargingSum(itemList.stream().mapToDouble(DailyBalanceItem::getQtyCharging).sum())
        ;
        return this.updateCascadeById(dailyBalance);
    }


    public Map<DailyOrderKey, InOutDTO> inOutUtil(List<String> ids) {
        Map<DailyOrderKey, InOutDTO> map = new HashMap<>();
        if (ids.isEmpty()) {
            return map;
        }
        List<DailyOrderItemDTO> list = workOrderItemService.workOrderItemOmoc(ids);
        Map<DailyOrderKey, List<DailyOrderItemDTO>> itemMap = list.stream().collect
                (Collectors.groupingBy(o -> new DailyOrderKey(o.getSupplierId(), o.getMatrId(), o.getFlagOmoc())));

        for (DailyOrderKey orderKey : itemMap.keySet()) {
            List<DailyOrderItemDTO> itemlist = itemMap.get(orderKey);
            double amountSum = itemlist.stream().mapToDouble(DailyOrderItemDTO::getAmount).sum();
            double qtySum = itemlist.stream().mapToDouble(DailyOrderItemDTO::getSkuQty).sum();
            InOutDTO inOutDTO = new InOutDTO();
            inOutDTO.setSumQmount(amountSum);
            inOutDTO.setSumQty(qtySum);
            map.put(orderKey, inOutDTO);
        }
        return map;
    }

    /**
     * 出库对比入库价格
     *
     * @param ids
     * @return
     */
    public Map<DailyOrderKey, InOutDTO> adjustmentPriceUtil(List<String> ids) {
        Map<DailyOrderKey, InOutDTO> map = new HashMap<>();
        Map<DailyOrderKey, List<DailyOrderItemDTO>> itemMap = new HashMap<>();
        if (ids.isEmpty()) {
            return map;
        }
        List<DailyOrderItemDTO> list = workOrderItemService.workOrderItemOmoc(ids);
        for (DailyOrderItemDTO dailyOrderItemDTO : list) {
            WorkOrderItem workOrderItem = workOrderItemService.getOne(Wrappers.<WorkOrderItem>query().eq("id", dailyOrderItemDTO.getLineId()));
            if (!dailyOrderItemDTO.getSkuPrice().equals(workOrderItem.getSkuPrice())) {
                DailyOrderKey dailyOrderKey = new DailyOrderKey(dailyOrderItemDTO.getSupplierId(), dailyOrderItemDTO.getMatrId(), dailyOrderItemDTO.getFlagOmoc());
                List<DailyOrderItemDTO> dailyrItems = new ArrayList<>();
                dailyrItems.add(dailyOrderItemDTO);
                itemMap.put(dailyOrderKey, dailyrItems);
            }
        }
        for (DailyOrderKey orderKey : itemMap.keySet()) {
            List<DailyOrderItemDTO> itemlist = itemMap.get(orderKey);
            double amountSum = itemlist.stream().mapToDouble(DailyOrderItemDTO::getAmount).sum();
            double qtySum = itemlist.stream().mapToDouble(DailyOrderItemDTO::getSkuQty).sum();
            InOutDTO inOutDTO = new InOutDTO();
            inOutDTO.setSumQmount(amountSum);
            inOutDTO.setSumQty(qtySum);
            map.put(orderKey, inOutDTO);
        }
        return map;
    }

    @Override
    public void delete(List<String> ids) {
        if (ids.isEmpty()) {
            return;
        }
        this.removeByIds(ids);
        dailyBalanceItemService.remove(Wrappers.<DailyBalanceItem>query().in("dept_daily_balance_id", ids));
    }

    @Override
    public Page<?> page(QueryContext context) {
        Map<String, Object> params = context.getParams();
        if (params == null) {
            return super.page(context);
        }

        Object warehouseId = params.get("warehouseId");
        if (warehouseId == null) {
            return super.page(context);
        }

        List<DailyBalance> list = this.list(Wrappers.<DailyBalance>query().eq("warehouse_id", warehouseId));
        if (list.isEmpty()) {
            throw new BusinessException("没有对应库存地");
        }
        return super.page(context);

    }

    @Override
    public void preview(String id, HttpServletResponse response) {
        response.setContentType(MediaType.APPLICATION_PDF_VALUE);

        DailyBalance dailyBalance = super.getById(id);
        if (dailyBalance == null) {
            return;
        }

        PdfExportParams exportParams = new PdfExportParams();
        exportParams.setTitle(String.format("%s日清单", dailyBalance.getDeptName()));
        exportParams.setPerRowsOfGrid(10);
        exportParams.setPageDirection(PdfPageDirection.TRANSVERSE);
        exportParams.setWatermark("本公司");


        exportParams.addHeaderItem("科室", dailyBalance.getDeptName(), 12)
                .addHeaderItem("日期", dailyBalance.getBalanceDate(), 12)
        ;

        exportParams.addGridColumn("rowIndex", "序号", 3)
                .addGridColumn("matrName", "材料名称", 9)
                .addGridColumn("matrSpec", "规格型号", 3)
                .addGridColumn("qtyBegin", "期初数量", 2)
                .addGridColumn("qtyEnd", "期末数量", 2)
                .addGridColumn("qtyIn", "入库数量", 2)
                .addGridColumn("qtyOut", "出库数量", 3);

        List<DailyBalanceItem> dailyBalanceItemList = dailyBalanceItemService.list(new QueryWrapper<DailyBalanceItem>()
                .eq("dept_daily_balance_id", id));
        if (dailyBalanceItemList.size() > 0) {
            exportParams.setGridData(dailyBalanceItemList);
        }

        exportParams.addFooterItem("制单人", dailyBalance.getCreator(), 8)
                .addFooterItem("经办人", dailyBalance.getModifier(), 8)
                .addFooterItem("科室签字:", "", 8);

        try {
            PdfExportUtils.export(response.getOutputStream(), exportParams);
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

    @Override
    public boolean saveDailyDalance(DailyBalance dailyBalance) {
        float days = DateUtils.getDateDiffOfDays(dailyBalance.getBalanceDate(), new Date());
        for (int i = 0; i <= days; i++) {
            Date date = DateUtils.addDays(dailyBalance.getBalanceDate(), i);
            String now = DateUtils.format(date, "yyyy-MM-dd");
            addDailyBalance(dailyBalance.getWarehouseId(), now);
        }
        return true;
    }


}
